Seznami in nizi


Mirko, Slavko in Blaise Pascal

1. podnaloga

Mirko in Slavko sta postala navdušena nad vsemi oblikami števil, zaporedij in podobno. Potem ko sta se naveličala preštevati, ali število zrn v krogih pri sončnici res ustreza Fibonaccijevemu zaporedju, sta naletela na funkcijo, ki naj bi vrnila seznam, ki vsebuje prvih n vrstic Pascalovega (binomskega) trikotnika (vrstica naj bo spet seznam).

  def trikotnik(n):
      '''Vrne n vrstic Pascalovega trikotnika'''
      celota = [[1]] # bo tole prav?
      zgoraj = []
      j = 0
      while j <= n:   # tu je nekaj narobe
          i = 1
          nova = [1]
          while i < len(zgoraj):
              nova = [zgoraj[i - 1] + zgoraj[i]] # hm, tole ...
              i = i + 1
          zgoraj = nova + [1]
          celota = celota + zgoraj # tudi tukaj
          j = j + 1 
      return celota

Žal (kot običajno) ima nekaj napak. Odpravi ji ... Na srečo je Mirko že intenzivno preiskušal funkcijo in odkril štiri možna mesta napak (glej oznake v kodi). Slavko pravi, da je Mirko pretiraval in da so napake zagotovo tri.

Uradna rešitev

def trikotnik(n):
    '''Vrne n vrstic Pascalovega trikotnika'''
    celota = [[1]] # začetna vrstica
    zgoraj = [] # prejšnja vrstica
    j = 0
    while j < n - 1:   
        i = 1
        nova = [1] # sestavimo novo vrstico
        while i < len(zgoraj): # na osnovi prejšnje sestavimo novo
            nova += [zgoraj[i - 1] + zgoraj[i]] # dodamo vsoto zgornjih členov
            i = i + 1
        zgoraj = nova + [1] # še zaključna enka - in si jo zapomnimo za naslednji korak
        celota = celota + [zgoraj] # dodamo v trikotnik
        j = j + 1 
    return celota

EMŠO

Številka EMŠO je sestavljena iz trinajst števk v obliki DDMMLLL50NNNX, pri čemer je DDMMLLL rojstni datum, 50 je koda registra, NNN je zaporedna številka in X kontrolna številka. Trimestna številka, NNN, je med 000 in 499 za moške ter med 500 in 999 za ženske.

1. podnaloga

Napiši funkcijo je_zenska(emso), ki za številko EMŠO, ki jo podamo kot niz, vrne True, če pripada ženski in False, če moškemu.

Primer:

>>> je_zenska('0505913509174')
True
>>> je_zenska('2109913502875')
False

Uradna rešitev

def je_zenska(emso):
    '''Ali EMSO predstavlja žensko'''
    return emso[9] >= "5"   # od 000 do 499 velja, da na 9. mestu stoji št. med 0 in 4
                            # od 500 do 999 velja, da na 9. mestu stoji št. med 5 in 9

2. podnaloga

Za okroglo mizo sedijo gosti. Nekateri so srečni, drugi ne. Konkretno: srečni so moški, ki sedijo med dvema ženskama in ženske, ki sedijo med dvema moškima. Razpored gostov podamo s seznamom njihovih številk EMŠO.

Napiši funkcijo stevilo_srecnezev(razpored), ki prejme takšen seznam in vrne število srečnih gostov.

Pazi: Ker je miza okrogla, sedi prvi gost poleg zadnjega. Če sedita moški in ženska sama za okroglo mizo, seveda nista srečneža.

Primer:

>>> stevilo_srecnezev(['0903912505707', '0110913506472', '2009956506012','1102946502619', '1902922506199', '2602930503913',
                       '0204940508783','1602960505003', '0207959502025', '0207962509545'])
4
>>> stevilo_srecnezev(['0702948501362', '1505987508785'])
0

Uradna rešitev

def stevilo_srecnezev(razpored):
    '''Koliko moških sedi med dvema ženskama, oz.
       koliko žensk sedi med dvema moškima'''
    srecnih = 0
    n = len(razpored)
    if n < 3: # če nimamo vsaj tri osebe, ni srečnih!
        return 0
    
    for i in range(n):
        pred = razpored[i-1]    # sedez pred i-tim sedezom
        tren = razpored[i]      # i-ti sedez
        nasl = razpored[(i+1) % n]      # sedez za i-tim sedezom
        
        if je_zenska(pred) != je_zenska(tren) != je_zenska(nasl):  # ce je na sredini moski, mora
            srecnih += 1                                  # imeti levo in desno zensko in obratno
            
    return srecnih

Dis... kva že?

Jeremija ves svoj prosti čas (torej ko ne spi, ko ne toži nad bolečinami, skratka kako minuto na dan) preživi kot moderator na nekem manj znanem forumu, ki ima kar lepo število registriranih uporabnikov, vendar večina med njimi piše same neumnosti. Med takimi je še posebno dejaven Tomaž Majer. Če se zdi Jeremiji objava žaljiva, jo nemudoma zbriše. Če objava na forumu ni žaljiva, ampak je le precej neumna, pa naredi naslednje: Vse samoglasnike odstrani iz besedila in jih (v enakem vrstnem redu) doda nazaj na konec. Na primer sporočilo:

Banana je najboljša! Izjemno dobra je tudi v kombinaciji z Nutelo!

postane

Bnn j njbljš! zjmn dbr j td v kmbncj z Ntl!aaaeaoaIeooaeuioiaiiueo

Takó besedilo postane težko berljivo in večina bralcev ga ignorira. Če pa se najde kdo, ki ga vsebina resnično zanima, lahko z nekaj truda razvozla vsebino sporočila.

Opisana transformacija besedila je znana pod imenom disemvoweling.

1. podnaloga

Napišite funkcijo prviSamoglasnik(niz), ki kot argument dobi niz niz in vrne položaj (indeks) prvega samoglasnika v danem nizu. Če v nizu ni samoglasnika, vrni -1

>>> prviSamoglasnik('Krščen matiček!')
4

Uradna rešitev

def prviSamoglasnik(niz):
    '''vrne položaj (indeks) prvega samoglasnika v danem nizu'''
    samoglasniki = "aeiouAEIOU"
    kje = 0
    for znak in niz:
        if znak in samoglasniki: # takoj pri prvem bomo zaključili zadevo
            return kje
        kje += 1
    return -1 # ni ga bilo 

def prviSamoglasnikDieHardPython(niz): #dieHard Pythonovci bi seveda zadevo napisali takole
    samoglasniki = "aeiou"
    return min(niz.index(c) for c in samoglasniki + samoglasniki.upper() if c in niz)

2. podnaloga

Napišite funkcijo disemvowel(sporocilo), ki kot argument prejme niz sporocilo. Funkcija naj sestavi in vrne nov niz, ki ga dobi iz niza sporocilo tako, da vse samoglasnike prestavi na konec niza.

>>> disemvowel('Banana je dobra!')
'Bnn j dbr!aaaeoa'

Uradna rešitev

def disemvowel(sporocilo):
    '''desemvowlanje sporočila'''
    brez = '' # niz brez samoglasnika
    samoglasniki = '' # vsi samoglasniki iz niza
    for c in sporocilo:
        if c in 'aeiouAEIOU': # gre za samoglasnik
            samoglasniki += c
        else: #"običajni" znak
            brez += c
    return brez + samoglasniki #zlepimo v pravem redu

3. podnaloga

Včasih pa želimo sporočilo dešifrirati, saj nas zanima njegova vsebina. Ta naloga je vse prej kot enostavna, saj ne vemo kam točno je treba vriniti izgnane samoglasnike. Sporočilo 'Bnn j dbr!aaaeoa' bi lahko dešifrirali tudi kot 'Banna ja debora!', kot 'Bnana aje dobar!' ipd.

Bob Rock je na mesta v besedilu, kjer predvideva, da bi morali stati samoglasniki, vstavil znake '*' (zvezdica). V sporočilu zvezdica nikoli ne bo imela drugega pomena. Prav tako bo število samoglasnikov enako številu zvezdic in vsi samoglasniki bodo na koncu niza.

Napišite funkcijo razveljaviDisemvowel(niz), ki bo zvezdice v nizu nadomestila s samoglasniki, ki se v nizu niz nahajajo na koncu. Zgled:

>>> razveljaviDisemvowel('B*n*n* j* d*br*!aaaeoa')
'Banana je dobra!'

Uradna rešitev

def razveljaviDisemvowel(niz):
    '''zvezdice v disemvowlanem nizu nadosmesti s samoglasniki s konca niza'''
    stZvezdic = niz.count('*') #da bomo vedeli, kje se začno samoglasniki
    if stZvezdic == 0: # ni kaj delati
        return niz 
    samoglasniki = niz[-stZvezdic:]  # odrežemo ustrezen del niza
    sporocilo = ''
    stevec = 0 # kje v nizu samogalsnikov smo
    for c in niz[:-stZvezdic]: # pregledamo le del niza brez končnih samoglasnikov
        if c == '*': # zvezdico nadomestimo z ustreznim samoglasnikom
            sporocilo += samoglasniki[stevec]
            stevec += 1 # premaknemo se naprej po samoglasnikih
        else:
            sporocilo += c
    return sporocilo

Cvetoče črke

1. podnaloga

Učenci so se v šoli v naravi med prostim časom zabavali z različnimi besednimi igrami. Ena izmed njih je bila ugibanje pomena čudnih besed. Pravilo je bilo, da so si zamišljali besede, ki vsebujejo črkovno zvezo 'pa' in jo nadomestili z zvezo 'cveti'. Tako so nastajale smešne besede kot na primer: cvetiprika, cveticvetigaj, nacvetidalec itd... Sestavi funkcijo cvetoceBesede(niz), ki bo vrnila pravi pomen teh besed.

Pri tem ne smeš uporabiti funkcije replace!

Na primer:

         >>>cvetoceBesede('cveticvetigaj')
         papagaj

Uradna rešitev

def cvetoceBesede(niz):
    '''izloči 'cveti' in nadomesti s 'pa' '''
    pravaBeseda = ''
    i = 0
    while i < len(niz) - 4: 
      if niz[i:i+5] == 'cveti':  # jemljemo po 5 znakov
          pravaBeseda += 'pa' # prvi dve zaporedni črki iz niza 'cveti' zamenja z 'pa', ostale tri pa izbriše
          i += 5
      else:
          pravaBeseda += niz[i]
          i += 1
    # prepišemo še mnorebitni preostanek
    while i < len(niz):
        pravaBeseda += niz[i]
        i += 1
    return pravaBeseda

def cvetoceBesedeV1(niz):
    '''izloči 'cveti' in nadomesti s 'pa' 
       uporaba ukaza del '''
    pravaBeseda=''
    if 'cveti' in niz: #ugotovi, če je v besedi besedna zveza "cveti" 
        sezCvet=list(niz)#niz spremeni v seznam
        i=0
        while i<len(sezCvet)-4: # prvi dve zaporedni črki iz niza 'cveti' zamenja z 'pa', ostale tri pa izbriše, nato pregleda še ostanek seznama
            if sezCvet[i:i+5]==['c', 'v', 'e', 't', 'i']:
                sezCvet[i]='p'
                sezCvet[i+1]='a'
                del sezCvet[i+2:i+5]
            i+=1
            pravaBeseda=''.join(sezCvet) #seznam zopet spremeni v niz in ga vrne
    else:
        return niz
    return pravaBeseda

def cvetoceBesedev2(niz):
    '''izloči 'cveti' in nadomesti s 'pa'
       uporabimo replace
    '''
    pravaBeseda = niz.replace('cveti', 'pa')
    return pravaBeseda

def cvetoceBesedev3(niz):
    '''izloči 'cveti' in nadomesti s 'pa'
       uporabimo split in join
    '''
    pravaBeseda = 'pa'.join(niz.split('cveti'))
    return pravaBeseda

2. podnaloga

V vasi Muhačevo so bili navdušeni pevci in so radi prepevali znano ljudsko: "Priletela muha na zid." Besedilo pesmi je povsem enostavno, saj se v bistvu ves čas ponavlja isto besedilo. Posebnost pesmi je v tem, da se najprej poje tako, da se vsi samoglasniki spremenijo v a, nato v e in tako naprej, dokler ne pridejo na vrsto vsi samoglasniki (a,e,i,o,u). Sestavi funkcijo muha(niz), ki bo vpisano besedo ali besedno zvezo spremenila v seznam besed, v katerih bodo najprej vsi samoglasniki a, nato e in še i, o in u. Beseda je napisana z malimi tiskanimi črkami.

Na primer:

   >>> muha('trobentica')
   ['trabantaca', 'trebentece', 'tribintici', 'trobontoco', 'trubuntucu'].

Uradna rešitev

def muha(niz):
    '''vrne seznam besed, v katerih so spremenjeni vsi samoglasniki
       najprej v a, nato v e in tako naprej'''
    vsiSamoglasniki = 'aeiou' 
    seznamBesed = []
    for samoglasnik in vsiSamoglasniki: # uporabimo vse samoglasnike
        novaBeseda = ''
        for znak in niz:  # pregledamo vse znake v nizu 
            if znak in vsiSamoglasniki: # če gre za poljuben samoglasnik
                novaBeseda += samoglasnik # dodamo trenutno 'aktivnega'
            else:
                novaBeseda += znak # sicer ga le prepišemo
        seznamBesed.append(novaBeseda) # beseda z ustreznim samoglasnikom
    return seznamBesed

3. podnaloga

Učiteljica je dala učencem nalogo naj napišejo tri besede tako, da se bo vsaka črka v vseh treh besedah pojavila natanko enkrat. Vsako novo pojavitev iste črke bo štela za napako. Torej, če se bo črka a pojavila trikrat, bosta to dve napaki. Sestavi funkcijo besede(niz), ki bo preštela napake in vrnila, koliko napak je učenec naredil. Pri tem so besede ločene z vejicami, ki niso del besed, vsi ostali zanki pa so!

Na primer:

      >>>besede('miza,miha,zima')
      7
      >>>besede('mimi,ali,gremo')
      4

Uradna rešitev

def besede(niz):
    '''funkcija prešteje, kolikokrat se posamezne črke pojavijo več kot enkrat'''
    koliko = 0
    črke = '' # katere črke so se že pojavile
    for znak in niz:
        if znak != ',': # vejice ignoriramo
            if znak in črke: # znak se je pojavil že prej
                koliko += 1
            else:
                črke += znak # nov znak
    return koliko

Škatle

Lojzku je dolgčas, zato se igra z velikimi praznimi škatlami, ki se nahajajo v skladišču, v katerem dela. Dimenzije škatel so shranjene v seznamu trojic. Na primer, trojica (50, 100, 100) predstavlja škatlo, ki je visoka 50 cm ter široka in dolga 100 cm.

S trojicami delamo tako kot s seznami, le spremijati ne moremo elementov. Torej tro[0] nam da prvi element iz trojice. Seveda pa lahko trojko tudi "razpakiramo"

   vis, sir, dol = skatla

če je skatla neka trojka.

1. podnaloga

Sestavite funkcijo stolp(skatle), ki vrne višino najvišjega stolpa, ki ga lahko sestavimo iz škatel, ne da bi jih obračali. Pri tem ni treba paziti na stabilnost stolpa. Na primer

  >>>stolp([(50, 100, 100), (60, 30, 50), (40, 40, 40)])
  150

Uradna rešitev

def stolp(skatle):
    '''Višina stolpa iz škatel, če škatle
       zlagamo po višini (prvi dimenziji)'''
    skupnaVišina = 0
    for skatla in skatle: # pregledamo vse škatle
        visina, sirina, dolzina = skatla
        skupnaVišina += visina
    return skupnaVišina

##Dodatno znanje nam da krajši zapis rešitve:
##def stolp(skatle):
##    return sum(v for v, _, _ in skatle)

2. podnaloga

Sestavite funkcijo najvisjiStolp(skatle), ki vrne višino najvišjega stolpa, ki ga lahko sestavimo iz škatel, če jih lahko obračamo. Pri tem še vedno ni treba paziti na stabilnost stolpa. Na primer, najvišji tak stolp iz škatel z dimenzijami (50, 100, 100), (60, 50, 50) in (40, 40, 40) bi imel višino 200 cm, saj bi prvo škatlo prevrnili na bok.

Uradna rešitev

def najvisjiStolp(skatle):
    '''Kako visok je lahko stolp, če lahko škatle obračamo''' 
    višina = 0
    for skatla in skatle:
        višina += max(skatla) # pobrali smo največjo dimenzijo
    return višina

##Dodatno znanje nam da krajši zapis rešitve:
##def najvisjiStolp(skatle):
##    return sum(max(v, s, d) for v, s, d in skatle)

3. podnaloga

Sestavite funkcijo greNotri(skatla1, skatla2), ki vrne True, če škatlo skatla1 lahko obrnemo tako, da gre v škatlo skatla2, torej da so dimenzije prve škatle strogo manjše od dimenzij druge škatle.

Uradna rešitev

def poVrsti(trojka):
    ''' uredimo trojko (a, b, c) po velikosti 
        (t1, t2, t3) so torej premešani a, b, c
        da velja t1 <= t2 <= t3 '''
    a, b, c = trojka
    mal = min(a, b, c)
    vel = max(a, b, c)
    sre = a + b + c - mal - vel
    return (mal, sre, vel)

def greNotri(skatla1, skatla2):
    '''Ali lahko skatlo1 damo v skatlo2'''
    nova1 = poVrsti(skatla1)
    nova2 = poVrsti(skatla2)
    
    return nova1[0] < nova2[0] and \
           nova1[1] < nova2[1] and \
           nova1[2] < nova2[2]

Ukleščeni nizi

1. podnaloga

Sestavite funkcijo prestej(niz), ki prešteje, koliko zašiljenih oklepajev tj. '<' in '>' je v nizu niz. Primer:

>>> prestej('<abc>>')
3
>>> prestej('a>>  x<Y>x  <<b')
6
>>> prestej('Koliko oklepajev > in < je v tem nizu?')
2

Pri tem ne smeš uporabiti vgrajene metode count

Uradna rešitev

def prestej(niz):
    '''Koliko je v nizu zašiljenih oklepajev'''
    koliko = 0
    iskaniZnaki = "><"
    for znak in niz:
        if znak in iskaniZnaki:
            koliko += 1
    return koliko

2. podnaloga

Niz imenujemo ukleščen, če se začne z predklepajem '<', konča z zaklepajem '>', vmes pa ni nobenega znaka za zašiljene oklepaje. Na primer, niz '<Zunaj sije sonce.>' je ukleščen niz.

Sestavite funkcijo uklescen(niz), ki preveri, ali je niz niz ukleščen. Primer:

>>> uklescen('<Zunaj sije sonce.>')
True
>>> uklescen('<   <3    >')
False
>>> uklescen('Zunaj sije sonce.')
False

Uradna rešitev

def uklescen(niz):
    '''Ali je niz ukleščen'''
    if len(niz) < 2: # niz je prekratek!
        return False
    return niz[0] == '<' and niz[-1] == '>' and prestej(niz) == 2

3. podnaloga

Sestavite funkcijo sklop(niz1, niz2), ki prejme dva ukleščena niza in vrne ukleščeni niz, ki predstavlja njun sklop. (Kaj je to sklop, je razvidno iz spodnjega primera.) Primer:

>>> sklop('<123>', '<456>')
'<123456>'
>>> sklop('<muca>', '<copatarica>')
'<mucacopatarica>'

Uradna rešitev

def sklop(niz1, niz2):
    '''Sklopimo dva ukleščena niza'''
    return niz1[:-1] + niz2[1:]

4. podnaloga

Sestavite funkcijo razlomi(niz, s), ki kot argumenta prejme ukleščeni niz niz in seznam nenegativnih celih števil s ter vrne seznam ukleščenih nizov, ki imajo dolžine, naštete v seznamu s, in skupaj tvorijo niz niz. Primer:

>>> razlomi('<Zunaj sije sonce.>', [2, 7, 8])
['<Zu>', '<naj sij>', '<e sonce.>']
>>> razlomi('<muca copatarica>', [4, 0, 11])
['<muca>', '<>', '< copatarica>']

Predpostavite lahko, da bo vsota števil v seznamu s enaka dolžini ukleščenega niza niz (če ne štejemo zašiljenih oklepajev).

Uradna rešitev

def razlomi(niz, s):
    '''Razlomi ukleščen niz na manjše koščke in vrne seznam koščkov'''
    rez = []
    k = 1 # kje začnemo ukleščeni niz
    for dolzina in s:
        rez.append('<' + niz[k:k+dolzina] + '>')
        k += dolzina
    return rez

Beseda je dala besedo

1. podnaloga

Sestavite funkcijo palindrom(niz), ki vrne True kadar je niz palindrom, in False sicer.

Uradna rešitev

def palindrom(niz):
    '''Ali je niz palindrom'''
    return niz == niz[::-1]

2. podnaloga

Sestavite funkcijo palindromHitro(niz), ki vrne True kadar je niz palindrom, in False sicer.

Če niz ni palindrom, naj to ugotovi kar se da hitro (npr. Matija očitno ni palibndrom, saj M ni enak a

Uradna rešitev

def palindromHitro(niz):
    '''Ali je niz palindrom'''
    for ind in range(1, len(niz) // 2 + 1):
        if niz[ind - 1] != niz[-ind]: # neujemajoča se znaka
            return False
    #vsi pari se ujemajo
    return True

3. podnaloga

Pravimo, da je beseda skoraj palindrom, če ji je treba zbrisati natanko eno črko, da bi postala palindrom. Primer je beseda 'kolo', ki ji moramo zbrisati črko 'k', pa postane palindrom 'olo'.

Sestavite funkcijo skorajPalindrom(niz), ki preveri, ali je niz skoraj palindrom. Vse znake (tudi presledke) v besedi obravnavamo enako.

Uradna rešitev

def skorajPalindrom(niz):
    '''ALi je niz skoraj palindrom'''
    # za vsak i poskusimo izpustiti črko na i-tem mestu
    for i in range(len(niz)):
        # če smo dobili palindrom, končamo
        if palindrom(niz[:i] + niz[i + 1:]):
            return True
    # če je zanka prišla do konca, palindroma nismo našli
    return False

4. podnaloga

Sestavite funkcijo vsebuje(niz1, niz2), ki za parametra dobi dva niza ter vrne True, če in samo če je mogoče prvi niz dobiti tako, da v drugi niz na poljubnih mestih vstavljamo dodatne znake. (povedano drugače, če prvi niz vsebuje vse znake iz drugega niza v ustreznem vrstnem redu).

Iz besede 'ROLA' lahko na primer z vrivanjem znakov dobimo besedo 'pRikOLicA'. Med velikimi in malimi tiskanimi črkami strogo ločujemo.

Uradna rešitev

def vsebuje(niz1, niz2):
    '''Ali je niz2 vsebovan v nizu1'''
    # v spremenljivki indeksNiz2 hranimo indeks znaka v malem nizu, ki ga iščemo
    indeksNiz2 = 0
    for znakNiz1 in niz1:
        # če smo našli znak na indeksu indeksNiz2, iščemo naslednjega
        if niz2[indeksNiz2] == znakNiz1:
            indeksNiz2 += 1
        # če smo našli vse znake v malem nizu, končamo
        if indeksNiz2 == len(niz2):
            return True
    # če se zanka konča, vseh znakov nismo našli
    return False

5. podnaloga

Sestavite funkcijo besede(niz), ki sestavi in vrne seznam parov indeksov (začetek, konec), ki določajo, kje v danem nizu se nahajajo posamezne besede. Beseda je maksimalno zaporedje znakov, ki ne vsebuje presledka.

>>> besede('Danes je lep dan, saj ne dežuje.')
[(0, 4), (6, 7), (9, 11), (13, 16), (18, 20), (22, 23), (25, 31)]
>>> besede(' abc  abc  abc ')
[(1, 3), (6, 8), (11, 13)]

Uradna rešitev

def besede(stavek):
    '''Seznam položajev besed'''
    indeksi = []
    # ali smo našli besedo, ali smo pri presledkih
    smo_v_besedi = False
    # zacetek trenutne besede
    zacetek = 0
    for i in range(len(stavek)):
        z = stavek[i]  # ta in prejšnji stavek lahko nadomestimo z enim
        # for i, z in enumerate(stavek):
        
        # če smo v besedi in najdemo presledek, dodamo njene indekse v seznam
        if smo_v_besedi and z == ' ':
            smo_v_besedi = False
            indeksi.append((zacetek, i - 1))
        # če nismo v besedi in najdemo znak, začnemo z besedo
        elif not smo_v_besedi and z != ' ':
            smo_v_besedi = True
            zacetek = i
    # če smo na koncu zanke še vedno v besedi, ta beseda sega do konca
    if smo_v_besedi:
        indeksi.append((zacetek, len(stavek) - 1))
    return indeksi